// Texas A&M University          //



// Modified by:  Mitchell Kline and Harshvardan
//          on:  April 25, 2007

// Top Level Architecture Model //

`include "IdealMemory.v"
`include "lshift2.v"
`include "ALU_behav.v"
`include "control.v"
`include "extend.v"
`include "Register_file.v"
`include "mux.v"
`include "Forwarding_Unit.v"

/*-------------------------- CPU -------------------------------
 * This module implements a single-cycle
 * CPU similar to that described in the text book 
 * (for example, see Figure 5.19). 
 *
 */

//
// Input Ports
// -----------
// clock - the system clock (m555 timer).
//
// reset - when asserted by the test module, forces the processor to 
//         perform a "reset" operation.  (note: to reset the processor
//         the reset input must be held asserted across a 
//         negative clock edge).
//   
//         During a reset, the processor loads an externally supplied
//         value into the program counter (see startPC below).
//   
// startPC - during a reset, becomes the new contents of the program counter
//	     (starting address of test program).
// 
// Output Port
// -----------
// dmemOut - contains the data word read out from data memory. This is used
//           by the test module to verify the correctness of program 
//           execution.
//-------------------------------------------------------------------------

module PipelinedProc(CLK, Reset_L, startPC, dmemOut);
   input 	Reset_L, CLK;
   input [31:0] startPC;
   output [31:0] dmemOut;

//-------------------------NET DEFINITIONS----------------------------------
//---General Format:  <wire_name><stage> with stage being a roman numeral---

wire [31:0] Next_PC;
wire [31:0] PC_plus4, PC_plus4i, PC_plus4ii, PC_plus4iii, PC_plus4iv;

wire [31:0] IWi, IWii;
wire [4:0] IWiii_2521;
wire [4:0] IWiii_2016;   // 2016 indicates it came from IWii[20:16]
wire [4:0] IWiii_1511;

wire PCSrc;
wire Jumpii, Jumpiii, Jumpiv, Jump;
wire Branchii, Branchiii, Branchiv;
wire MemReadii, MemReadiii, MemReadiv;
wire MemtoRegii, MemtoRegiii, MemtoRegiv, MemtoReg;
wire MemWriteii, MemWriteiii, MemWriteiv;
wire ALUSrcii, ALUSrciii;
wire RegWriteii, RegWriteiii, RegWriteiv, RegWrite;
wire [3:0] ALUOpii, ALUOpiii;
wire Signedii;

wire [31:0] Reg1_outii, Reg1_outiii;
wire [31:0] Reg2_outii, Reg2_outiii, Reg2_outiiia, Reg2_outiv;
wire [31:0] ImmValue32ii, ImmValue32iii, ImmValue32iii_Shifted;
wire [31:0] Shifted_IWii;
wire [31:0] JumpAddrii, JumpAddriii;
wire [31:0] FinalJumpAddriii;

wire [31:0] ALUSrc1, ALUSrc2, ALUSrc2_0;
wire  zeroiii, zeroiv;
wire [31:0] Resultiii, Resultiv, Result;
wire [4:0] RegDstSrc0;
wire [4:0] RegWriteAddriii, RegWriteAddriv, RegWriteAddr;
wire [31:0] BranchAddriii, BranchAddriv;

wire [31:0] Mem_outiv, Mem_out;
wire [31:0] DataSrc0;
wire [31:0] RegWriteData;
wire [31:0] JumpMUXSrc0;

wire [1:0] ALUSrcSel1;
wire [1:0] ALUSrcSel2;
wire [1:0] SWSrcSel;

//----------------------------------------REGISTER DEFINITIONS----------------------------------------

reg [31:0] PC;
reg [63:0] IFID;
reg [184:0] IDEX;
reg [139:0] EXMEM;
reg [103:0] MEMWB;

//-----------------------------------------MODULE INSTANTIATIONS---------------------------------------

InstrMem Instruction_Memory(
  .Mem_Addr(PC),
  .Dout(IWi)
);

DataMem Data_Memory(
  .Mem_Addr(Resultiv),
  .CLK(CLK),
  .Mem_rd(MemReadiv),
  .Mem_wr(MemWriteiv),
  .Mem_DIN(Reg2_outiv),
  .Mem_DOUT(Mem_outiv)
);

REGS Registers(
  .busA(Reg1_outii),
  .busB(Reg2_outii),
  .busW(RegWriteData),
  .RA(IWii[25:21]),
  .RB(IWii[20:16]),
  .RW(RegWriteAddr),
  .RegWr(RegWrite),
  .clk(CLK)
);

ALU_behav ALU(
  .ADin(ALUSrc1),
  .BDin(ALUSrc2),
  .ALU_ctr(ALUOpiii),
  .Result(Resultiii),
  .Overflow(),
  .Carry_in(1'b0),
  .Carry_out(),
  .Zero(zeroiii)
);

control cntrl(
  .RegWrite(RegWriteii),
  .ALUSrc(ALUSrcii),
  .ALUOp(ALUOpii),
  .MemWrite(MemWriteii),
  .MemRead(MemReadii),
  .MemtoReg(MemtoRegii),
  .Branch(Branchii),
  .RegDest(RegDestii),
  .Jump(Jumpii),
  .Signed(Signedii),
  .IW(IWii)
);

lshift2 ShiftJumpAddr(IWii, Shifted_IWii);
lshift2 ShiftBranchAddr(ImmValue32iii, ImmValue32iii_Shifted);

extend ImmExtend(IWii[15:0], Signedii, ImmValue32ii);

MUX5_2to1 RegDstMUX1(IWiii_2016, IWiii_1511, RegDestiii, RegDstSrc0);
MUX5_2to1 RegDstMUX2(RegDstSrc0, 5'd31, Jumpiii, RegWriteAddriii);

MUX32_2to1 PCSrcMUX(PC_plus4i, BranchAddriv, PCSrc, JumpMUXSrc0);
MUX32_2to1 JumpSrcMUX2(JumpMUXSrc0, FinalJumpAddriii, Jumpiii, Next_PC);
MUX32_2to1 JumpSrcMUX1(JumpAddriii, ALUSrc1, ALUSrciii, FinalJumpAddriii);

MUX32_2to1 ALUSrcMUX(ALUSrc2_0, ImmValue32iii, ALUSrciii, ALUSrc2);
MUX32_3to1 ALUSrc1MUX(Reg1_outiii, Mem_out, Resultiv, ALUSrcSel1, ALUSrc1);
MUX32_3to1 ALUSrc2MUX(Reg2_outiii, Mem_out, Resultiv, ALUSrcSel2, ALUSrc2_0);
MUX32_3to1 SWSrcMUX(Reg2_outiii, Mem_out, Resultiv, SWSrcSel, Reg2_outiiia);

MUX32_2to1 DataMUX1(Result, Mem_out, MemtoReg, DataSrc0);
MUX32_2to1 DataMUX2(DataSrc0, PC_plus4, Jump, RegWriteData);

Forwarding_Unit FU(
  ALUSrcSel1, 
  ALUSrcSel2,
  SWSrcSel,
  IWiii_2521, 
  IWiii_2016, 
  RegWriteAddriv, 
  RegWriteAddr, 
  RegWriteiv, 
  MemtoReg, 
  RegDestiii
);

//-----------------------------------(PSEUDO)STRUCTURAL CODE----------------------------------------------

assign PC_plus4i = PC + 4;
assign BranchAddriii = PC_plus4iii + ImmValue32iii_Shifted;
assign JumpAddrii = {PC_plus4ii[31:28],Shifted_IWii[27:0]};
and BranchAnd(PCSrc, zeroiv, Branchiv);

assign PC_plus4ii = IFID[63:32];
assign IWii = IFID[31:0];

assign IWiii_2521 = IDEX[184:180];
assign JumpAddriii = IDEX[179:148];
assign PC_plus4iii = IDEX[139:108];
assign RegDestiii = IDEX[107];
assign Jumpiii = IDEX[106];
assign Branchiii = IDEX[105];
assign MemReadiii = IDEX[104];
assign MemtoRegiii = IDEX[103];
assign MemWriteiii = IDEX[102];
assign ALUSrciii = IDEX[101];
assign RegWriteiii = IDEX[100];
assign ALUOpiii = IDEX[99:96];
assign Reg1_outiii = IDEX[95:64];
assign Reg2_outiii = IDEX[63:32];
assign ImmValue32iii = IDEX[31:0];
assign IWiii_2016 = IDEX[143:140];
assign IWiii_1511 = IDEX[147:144];


assign PC_plus4iv = EXMEM[139:108];
assign Jumpiv = EXMEM[75];
assign BranchAddriv = EXMEM[107:76];
assign MemReadiv = EXMEM[73];
assign Branchiv = EXMEM[74];
assign zeroiv = EXMEM[69];
assign MemtoRegiv = EXMEM[72];
assign MemWriteiv = EXMEM[71];
assign RegWriteiv = EXMEM[70];
assign Resultiv = EXMEM[68:37];
assign Reg2_outiv = EXMEM[36:5];
assign RegWriteAddriv = EXMEM[4:0];

assign PC_plus4 = MEMWB[103:72];
assign Jump = MEMWB[71];
assign MemtoReg = MEMWB[70];
assign RegWrite = MEMWB[69];
assign Mem_out = MEMWB[68:37];
assign Result = MEMWB[36:5];
assign RegWriteAddr = MEMWB[4:0];

assign dmemOut = Mem_out;

//-------------------------------------BEHAVORAL CODE------------------------------------------------------

always @ (negedge CLK) begin

  IFID[63:32] = PC_plus4i;
  IFID[31:0] = IWi;

  IDEX[31:0] = ImmValue32ii;
  IDEX[63:32] = Reg2_outii;
  IDEX[95:64] = Reg1_outii;
  IDEX[99:96] = ALUOpii;
  IDEX[100] = RegWriteii;
  IDEX[101] = ALUSrcii;
  IDEX[102] = MemWriteii;
  IDEX[103] = MemtoRegii;
  IDEX[104] = MemReadii;
  IDEX[105] = Branchii;
  IDEX[106] = Jumpii;
  IDEX[107] = RegDestii;
  IDEX[139:108] = PC_plus4ii;
  IDEX[143:140] = IWii[20:16];
  IDEX[147:144] = IWii[15:11];
  IDEX[179:148] = JumpAddrii;
  IDEX[184:180] = IWii[25:21];


  EXMEM[139:108] = PC_plus4iii;
  EXMEM[107:76] = BranchAddriii;
  EXMEM[75:69] = {Jumpiii, Branchiii, MemReadiii, MemtoRegiii, MemWriteiii, RegWriteiii, zeroiii};
  EXMEM[68:37] = Resultiii;
  EXMEM[36:5] = Reg2_outiiia;
  EXMEM[4:0] = RegWriteAddriii;

  MEMWB[103:72] = PC_plus4iv;
  MEMWB[71:69] = {Jumpiv, MemtoRegiv, RegWriteiv};
  MEMWB[68:37] = Mem_outiv;
  MEMWB[36:5] = Resultiv;
  MEMWB[4:0] = RegWriteAddriv;

end

always @ (posedge CLK) begin
  if (!Reset_L) begin 
    PC = startPC;
    IFID = 0;
    IDEX = 0;
    EXMEM = 0;
    MEMWB = 0;
  end
  else PC = Next_PC;
end

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Debugging threads that you may find helpful (you may have
// to change the variable names).
//
   //  Monitor changes in the program counter
/*   initial  $display("\t\t\ttime,\t\tPC,\top,\trs,\trt,\trd,\timm16,\tfunct,\t\t$ra");

   always @(PC)
     #10 $display("\t%d,\t%d,\t%d,\t%d,\t%d,\t%d,\t%d,\t%d,\t%d", $time,
         PC,Instruction[31:26],Instruction[25:21],Instruction[20:16],
         Instruction[15:11],Instruction[15:0],Instruction[5:0],Registers.regs[31]);
*/
/*  // Monitor ALU
  initial $display("\t\t\ttime,\t\tin1,\t\tin2,\tcontrol,\tresult,\tzero,\tcarry out");
  always @ (Reg1_out or ALUSrcData2 or ALU_control)
    #10 $display("\t%d,\t%d,\t%d,\t%d,\t%d,\t%d,\t%d",$time,Reg1_out,ALUSrcData2,ALU_control,Result,Zero,Carry_out);
*/
   /*   Monitors memory writes
   always @(MemWrite)
	begin
	#1 $display($time," MemWrite=%b clock=%d addr=%d data=%d", 
	            MemWrite, clock, dmemaddr, rportb);
	end
   */
  initial $display("\t\t\ttime,\tPC,\t\t$1,\t\t$2,\t\t$3,\t\t$4,\t\t$5,\t\t$6,\t\t$7,\t\t$8");
  // Monitor Registers
  always @ (PC) begin
      #1 $display("\t%d,\t%h,\t%h,\t%h,\t%h,\t%h,\t%h,\t%h,\t%h,\t%h",$time,
           PC,
           Registers.regs[1],
           Registers.regs[2],
           Registers.regs[3],
  //         IWii[25:21],
  //         Reg1_outii,
  //         IDEX[95:64],
           Registers.regs[4],
           Registers.regs[5],
           Registers.regs[6],
           Registers.regs[7],
           Registers.regs[8],
         );
   end

/*  initial $display("\tPC,\tRegWrite");
  always @ (PC) begin
    #1 $display("\t%0h,\t%0b", PC, RegWrite);
  end   
*/
endmodule // CPU


module m555 (CLK);
   parameter StartTime = 0, Ton = 50, Toff = 50, Tcc = Ton+Toff; // 
 
   output CLK;
   reg 	  CLK;
   
   initial begin
      #StartTime CLK = 0;
   end
   
   // The following is correct if clock starts at LOW level at StartTime //
   always begin
      #Toff CLK = ~CLK;
      #Ton CLK = ~CLK;
   end
endmodule

   
module testCPU(Reset_L, startPC, testData);
   input [31:0] testData;
   output 	Reset_L;
   output [31:0] startPC;
   reg 		 Reset_L;
   reg [31:0] 	 startPC;
   

   initial begin
   
      // Your program 1
      Reset_L = 0;  startPC = 0 * 4;
      #101 // insures reset is asserted across negative clock edge
      Reset_L = 1; 

         
      #1500; // allow enough time for program 1 to run to completion
      #1 $display ("Program 1: Result: %d", testData);
      
      // Your program 2
      Reset_L = 0;
      startPC = 14 * 4;
      #101 Reset_L = 1; 
      #1500;

      #1 $display ("Program 2: Result: %d", testData);
      
      // Program 3
      Reset_L = 0;  startPC= 30 * 4;
      #101 Reset_L = 1;
      #2000;

      #1 $display ("Program 3: Result: %d", testData);

      // Program 4 (enable register viewing to see results)
      Reset_L = 0;  startPC =60 * 4;
      #101 Reset_L = 1;
      #2000;

      #1 $display ("Program 4: END");


      // Program 5
      Reset_L = 0;  startPC = 71*4;
      #101 Reset_L = 1;
      #2000;

      #1 $display ("Program 5: Result: %h",testData);

      // Program 6
      Reset_L = 0;  startPC = 90*4;
      #101 Reset_L = 1;
      #2000

      #1 $display ("Program 6: END");



      $finish;
   
   end endmodule // testCPU

module TopProcessor;
   wire reset, CLK, Reset_L;
   wire [31:0] startPC;
   wire [31:0] testData;
   
   m555 system_clock(CLK);
   PipelinedProc SSProc(CLK, Reset_L, startPC, testData);
   testCPU tcpu(Reset_L, startPC, testData); 

endmodule // TopProcessor
